﻿#include "mainform.h"
#include "ui_mainform.h"
#include <QDir>
#include <QGraphicsDropShadowEffect>
#include <QProgressDialog>
#include <QSettings>
#include <QTextCodec>
#include <Alg/Api_WoodDetect.h>
#include <QTimer>
#include <QDateTime>
#include <QPainter>
#include <QMessageBox>
#include <QFileDialog>
#include <QtGlobal>


MainForm::MainForm(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::MainForm)
{
    ui->setupUi(this);
    initializePage();
    initializeData();
    initCameras();
    enableLocalMode = false;
    m_sql.init("MainForm_db");
}

MainForm::~MainForm()
{
    delete ui;
}

void MainForm::initializePage()
{
    this->setWindowFlags(Qt::FramelessWindowHint);
    this->setAttribute(Qt::WA_TranslucentBackground,true);


    //内层窗口添加对应的阴影效果
    QGraphicsDropShadowEffect *shadow_effectA = new QGraphicsDropShadowEffect(this);
    QGraphicsDropShadowEffect *shadow_effectB = new QGraphicsDropShadowEffect(this);
    QGraphicsDropShadowEffect *shadow_effectC = new QGraphicsDropShadowEffect(this);
    QGraphicsDropShadowEffect *shadow_effectD = new QGraphicsDropShadowEffect(this);

    shadow_effectA->setOffset(0, 0);
    shadow_effectA->setColor(QColor(150,150,150));
    shadow_effectA->setBlurRadius(6);

    shadow_effectB->setOffset(0, 0);
    shadow_effectB->setColor(QColor(150,150,150));
    shadow_effectB->setBlurRadius(6);

    shadow_effectC->setOffset(0, 0);
    shadow_effectC->setColor(QColor(150,150,150));
    shadow_effectC->setBlurRadius(6);

    shadow_effectD->setOffset(0, 0);
    shadow_effectD->setColor(QColor(150,150,150));
    shadow_effectD->setBlurRadius(6);


    ui->main_gBox_1->setGraphicsEffect(shadow_effectA);
    ui->main_gBox_2->setGraphicsEffect(shadow_effectB);
    ui->main_gBox_3->setGraphicsEffect(shadow_effectC);
    ui->main_gBox_4->setGraphicsEffect(shadow_effectD);


    m_nStart = 1;

}

void MainForm::initializeData()
{
    CreateCameraSaveFile();
    // 读取面阵相机ROI配置
    readAreaCameraConfig();

    //注册数据类型 不然信号槽会数据丢失
    qRegisterMetaType<CFrameInfo>("CFrameInfo");

    m_tSaveImg1 = new SaveImgThread();
    m_tSaveImg2 = new SaveImgThread();
    m_tSaveImg3 = new SaveImgThread();
    m_tSaveImg4 = new SaveImgThread();
    m_tSaveImg5 = new SaveImgThread();
    m_tSaveImg6 = new SaveImgThread();

    m_SaveForm = new SaveForm();
    m_SaveForm->hide();

    m_woodProcessor = new WoodDetectProcessor(this);
    connect(m_woodProcessor, &WoodDetectProcessor::processingFinished,
            this, &MainForm::handleAlgorithmResult);
    connect(m_woodProcessor, &WoodDetectProcessor::algorithmError,
            this, &MainForm::handleError);
    // 结果检查定时器
    QTimer *resultTimer = new QTimer(this);
    connect(resultTimer, &QTimer::timeout,
            this, &MainForm::checkAlgorithmResults);
    resultTimer->start();

    connect(m_SaveForm, &SaveForm::sigSaveState,
            this, &MainForm::updateSaveStates);
}

void MainForm::initCameras()
{
    int ret = IMV_OK;
    // 连接相机之前不显示状态栏 | Don't show status bar before connecting camera
    ui->info_ccd_1->setText("");
    ui->info_ccd_2->setText("");
    ui->info_ccd_3->setText("");
    ui->info_ccd_4->setText("");
    ui->info_ccd_5->setText("");
    ui->info_ccd_6->setText("");

    ret = IMV_EnumDevices(&m_deviceInfoList, interfaceTypeAll);
    if (IMV_OK != ret)
    {
        printf("Enumeration devices failed! ErrorCode[%d]\n", ret);
        return;
    }
    if (m_deviceInfoList.nDevNum < 1)
    {
        ui->Bt_start->setEnabled(false);
    }
    else
    {
        ui->Bt_start->setEnabled(true);
        createCameraThread(m_deviceInfoList);
    }

    if (m_tSaveImg5) {
        m_tSaveImg5->m_worker->setRoi(m_areaRoi5);
    }

    if (m_tSaveImg6) {
        m_tSaveImg6->m_worker->setRoi(m_areaRoi6);
    }
}

void MainForm::createCameraThread(IMV_DeviceList m_deviceInfoList)
{
    for (unsigned int i = 0; i < m_deviceInfoList.nDevNum; i++)
    {
        QString cameraKey = m_deviceInfoList.pDevInfo[i].cameraKey;

        QString cameraName = QString::fromLocal8Bit(m_deviceInfoList.pDevInfo[i].cameraName);
        QString ccdInfo;
        CameraThread* thread = new CameraThread(cameraKey);
        connect(thread, &CameraThread::frameReceived, this, &MainForm::handleFrame);
        connect(thread, &CameraThread::woodFrameReady, this, &MainForm::woodFrame);
        connect(thread, &CameraThread::errorOccurred, this, &MainForm::handleError);



        cameraThreads.insert(cameraKey, thread);
        ccdInfo = QString("%1[%2]").arg(cameraKey).arg(cameraName);
        if (cameraKey == "Machine Vision:EC48318AAK00038") {
            connect(thread->m_worker, &CameraWorker::woodFrameReady, m_tSaveImg1->m_worker, &SaveImgWorker::woodFrame);
            m_tCamera1 = thread;
            thread->m_worker->m_nId = 1;
            ui->info_ccd_1->setText(ccdInfo);
        }

        if (cameraKey == "Huaray Technology:EB29285AAK00008") {
            connect(thread->m_worker, &CameraWorker::woodFrameReady, m_tSaveImg2->m_worker, &SaveImgWorker::woodFrame);

            m_tCamera2 = thread;
            thread->m_worker->m_nId = 2;
            ui->info_ccd_2->setText(ccdInfo);
        }

        if (cameraKey == "Huaray Technology:BK27071AAK00002") {
            connect(thread->m_worker, &CameraWorker::woodFrameReady, m_tSaveImg3->m_worker, &SaveImgWorker::woodFrame);
            m_tCamera3 = thread;
            thread->m_worker->m_nId = 3;
            ui->info_ccd_3->setText(ccdInfo);
        }

        if (cameraKey == "Huaray Technology:DA56668AAK00003") {
            connect(thread->m_worker, &CameraWorker::woodFrameReady, m_tSaveImg4->m_worker, &SaveImgWorker::woodFrame);
            m_tCamera4 = thread;
            thread->m_worker->m_nId = 4;
            ui->info_ccd_4->setText(ccdInfo);
        }

        if (cameraKey == "Machine Vision:DL00908CAK00014") {
            m_tCamera5 = thread;
            connect(thread->m_worker, &CameraWorker::frameReceived, m_tSaveImg5->m_worker, &SaveImgWorker::handleFrame);
            thread->m_worker->m_isAreaScan = true;
            thread->m_worker->m_nId = 5;
            ui->info_ccd_5->setText(ccdInfo);
        }

        if (cameraKey == "Machine Vision:DL00908CAK00005") {
            m_tCamera6 = thread;
            connect(thread->m_worker, &CameraWorker::frameReceived, m_tSaveImg6->m_worker, &SaveImgWorker::handleFrame);
            thread->m_worker->m_isAreaScan = true;
            thread->m_worker->m_nId = 6;
            ui->info_ccd_6->setText(ccdInfo);
        }
        thread->initialize();
        ui->Bt_start->setEnabled(true);
    }
}

void MainForm::releaseAllCameras()
{
    for (auto* thread : std::as_const(cameraThreads)) {
        thread->stop();
        delete thread;
    }
    cameraThreads.clear();
}

void MainForm::processWoodImages()
{
    int m_woodFrameIndex = 1;
    const size_t totalFrames = m_vListC1.size();

    if (m_vListC2.size() != totalFrames ||
        m_vListC3.size() != totalFrames ||
        m_vListC4.size() != totalFrames)
    {
        throw std::runtime_error("Inconsistent channel data size");
    }

    for (size_t i = 0; i < totalFrames; ++i)
    {
        cv::Mat& frame1 = m_vListC1[i];
        cv::Mat& frame2 = m_vListC2[i];
        cv::Mat& frame3 = m_vListC3[i];
        cv::Mat& frame4 = m_vListC4[i];

        if (!frame1.isContinuous() || !frame2.isContinuous() ||
            !frame3.isContinuous() || !frame4.isContinuous())
        {
            throw std::runtime_error("Non-continuous matrix detected");
        }
        std::vector<void*> srcptrlist{
            frame1.data,
            frame2.data,
            frame3.data,
            frame4.data
        };

        std::vector<unsigned int> srcwidthlist{
            static_cast<unsigned int>(frame1.cols),
            static_cast<unsigned int>(frame2.cols),
            static_cast<unsigned int>(frame3.cols),
            static_cast<unsigned int>(frame4.cols)
        };

        std::vector<unsigned int> srcheightlist{
            static_cast<unsigned int>(frame1.rows),
            static_cast<unsigned int>(frame2.rows),
            static_cast<unsigned int>(frame3.rows),
            static_cast<unsigned int>(frame4.rows)
        };

        int woodFlag = WoodMidt;
        if (m_woodFrameIndex == 1) {
            woodFlag = WoodFront;
        }
        else if (m_woodFrameIndex >= CaptureNum) {
            woodFlag = WoodRear;
        }

        try {
            /*m_woodProcessor->processWoodImages(
                srcptrlist.data(),
                srcwidthlist.data(),
                srcheightlist.data(),
                0.5f,
                woodFlag);*/
        }
        catch (const std::exception& e) {
            std::cerr << "Processing failed: " << e.what() << std::endl;
            throw;
        }
        m_woodFrameIndex++;
    }
}

void MainForm::CreateCameraSaveFile()
{
    const QString imgPath = QDir::currentPath() + "/Pic";
    QDir dir;

    // 如果根目录不存在就一次性建好
    if (!dir.exists(imgPath))
    {
        dir.mkpath(imgPath);               // 一次性建好 Pic
        const QStringList subFolders = {
            "OK", "NG", "Source Image", "Composite Image"
        };

        // 1~6 号相机
        for (int i = 1; i <= 6; ++i)
        {
            const QString ccdRoot = imgPath + QString("/ccd%1").arg(i);
            for (const QString &sub : subFolders)
                dir.mkpath(ccdRoot + "/" + sub);
        }
    }
}


int MainForm::getCameraID(const QString& cameraKey)
{
    static const QHash<QString,int> tbl = {
        {"Machine Vision:EC48318AAK00038", 1},
        {"Huaray Technology:EB29285AAK00008", 2},
        {"Huaray Technology:BK27071AAK00002", 3},
        {"Huaray Technology:DA56668AAK00003", 4},
        {"Machine Vision:DL00908CAK00014", 5},
        {"Machine Vision:DL00908CAK00005", 6}
    };
    return tbl.value(cameraKey, 0);   // 找不到返回 0
}

void MainForm::stopAll()
{
    releaseAllCameras();
    m_woodProcessor->stopWorkerThreads();
}



void MainForm::handleFrame(CFrameInfo frameInfo)
{
    m_captureTime = QDateTime::currentMSecsSinceEpoch();
    QImage::Format format = QImage::Format_Invalid;
    if (frameInfo.m_ePixelType == gvspPixelMono8) {
        format = QImage::Format_Grayscale8;
    } else if (frameInfo.m_ePixelType == gvspPixelRGB8) {
        format = QImage::Format_RGB888;
    }

    if (format == QImage::Format_Invalid) {
        qWarning() << "Unsupported pixel format:" << frameInfo.m_ePixelType;
        return;
    }

    QImage image(
        frameInfo.m_pImageBuf.get(),
        frameInfo.m_nWidth,
        frameInfo.m_nHeight,
        format
        );

    if (!image.isNull()) {
        // 转换为可绘制的格式
        QPixmap pixmap = QPixmap::fromImage(image);
        QPainter painter(&pixmap);
        painter.setPen(QPen(Qt::red, 2));

        // 根据相机ID绘制ROI框
        switch (getCameraID(frameInfo.cameraKey)) {
        case 5:
            if (!m_areaRoi5.isEmpty()) {
                painter.drawRect(m_areaRoi5);
            }
            ui->lm_ccd_5->setPixmap(pixmap);
            break;
        case 6:
            if (!m_areaRoi6.isEmpty()) {
                painter.drawRect(m_areaRoi6);
            }
            ui->lm_ccd_6->setPixmap(pixmap);
            break;
        default:
            break;
        }
    }
}

void MainForm::woodFrame(CFrameInfo frameInfo)
{
    // 根据相机ID选择存储的容器
    auto& targetList = [&]() -> std::vector<cv::Mat>& {
        switch (getCameraID(frameInfo.cameraKey)) {
        case 1: return m_vListC1;
        case 2: return m_vListC2;
        case 3: return m_vListC3;
        case 4: return m_vListC4;
        default:
            static std::vector<cv::Mat> dummy;
            qWarning() << "Invalid camera ID:" << frameInfo.cameraKey;
            return dummy;
        }
    }();

    // 转换为Mat
    cv::Mat frameMat;
    switch(frameInfo.m_ePixelType) {
    case gvspPixelRGB8:
        frameMat = cv::Mat(
                       frameInfo.m_nHeight,
                       frameInfo.m_nWidth,
                       CV_8UC3,
                       frameInfo.m_pImageBuf.get()
                       ).clone();
        break;
    case gvspPixelMono8:  // 单通道直接存储
        frameMat = cv::Mat(
                       frameInfo.m_nHeight,
                       frameInfo.m_nWidth,
                       CV_8UC1,
                       frameInfo.m_pImageBuf.get()
                       ).clone();
        break;
    default:
        qWarning() << "Unsupported format:" << frameInfo.m_ePixelType;
        return;
    }

    targetList.push_back(std::move(frameMat));

    // 检查是否集齐4个线扫相机的图像
    if (m_vListC1.size() == CaptureNum &&
        m_vListC2.size() == CaptureNum &&
        m_vListC3.size() == CaptureNum &&
        m_vListC4.size() == CaptureNum)
    {
        m_processStartTime = QDateTime::currentMSecsSinceEpoch();
        processWoodImages();

        // 清空缓存
        m_vListC1.clear();
        m_vListC2.clear();
        m_vListC3.clear();
        m_vListC4.clear();
    }
}

void MainForm::handleError(QString message)
{
    qDebug()<< message;
}

void MainForm::handleAlgorithmResult(int cameraId, const QImage &result)
{
    if(enableLocalMode){
        emit sigAlgTestResult(result);
    }else{
        switch (cameraId) {
        case 1:ui->lm_ccd_1->setPixmap(QPixmap::fromImage(result));break;
        case 2:ui->lm_ccd_2->setPixmap(QPixmap::fromImage(result));break;
        case 3:ui->lm_ccd_3->setPixmap(QPixmap::fromImage(result));break;
        case 4:ui->lm_ccd_4->setPixmap(QPixmap::fromImage(result));break;
        default:
            return;
        }
    }

    SaveImgThread* tSave = nullptr;
    switch (cameraId) {
    case 1:
        if(!m_tSaveImg1->m_worker->m_cSave.m_nComposite){return;}
        tSave = m_tSaveImg1;
        break;
    case 2:
        if(!m_tSaveImg2->m_worker->m_cSave.m_nComposite){return;}
        tSave = m_tSaveImg2;
        break;
    case 3:
        if(!m_tSaveImg3->m_worker->m_cSave.m_nComposite){return;}
        tSave = m_tSaveImg3;
        break;
    case 4:
        if(!m_tSaveImg4->m_worker->m_cSave.m_nComposite){return;}
        tSave = m_tSaveImg4;
        break;
    default:
        break;
    }
    // 构建保存路径
    QString appDir = QCoreApplication::applicationDirPath();
    QString savePath =  QString("%1/Pic/ccd%2/Composite Image/").arg(appDir).arg(cameraId);


    QString fileName = QString("Pic%1_%2_%3.jpg")
                           .arg(cameraId).arg(QDateTime::currentDateTime().toString("yyyyMMdd_hhmmss")).arg(tSave->m_worker->m_nIndexM);
    QString filePath = savePath + fileName;

    // 保存为JPEG格式
    if (result.save(filePath, "JPEG")) {
        // qDebug() << "Image saved to:" << filePath;
        tSave->m_worker->m_nIndexM++;
    } else {
        qWarning() << "Failed to save image.";
    }
}

double MainForm::pixelsToInches(int pixels, int dpi) {
    if (dpi == 0) {
        qWarning("DPI cannot be zero.");
        return 0.0;
    }
    return static_cast<double>(pixels) / dpi;
}

/**
 * @brief 将像素值转换为毫米（保留 2 位小数）
 * @param pixels 像素值
 * @param dpi 分辨率（每英寸像素数）
 * @return 毫米值（四舍五入到 2 位小数）
 */
double MainForm::pixelsToMm(int pixels, int dpi) {
    if (dpi <= 0) {
        qWarning() << "Invalid DPI value:" << dpi;
        return 0.0;
    }
    double inches = static_cast<double>(pixels) / dpi; // 像素→英寸
    double mm = inches * 25.4;                        // 英寸→毫米（1英寸=25.4mm）
    return roundToTwoDecimalPlaces(mm);               // 保留 2 位小数
}

void MainForm::drawRectangle(cv::Mat& image, const cv::Rect& rect, cv::Scalar color, int thickness)
{
    // 确保图像是彩色图像
    if (image.channels() == 1) {
        cv::cvtColor(image, image, cv::COLOR_GRAY2BGR);
    }
    cv::rectangle(image, rect, color, thickness);
}

void MainForm::drawRectangle(cv::Mat& image, cv::Point pt1, cv::Point pt2, cv::Scalar color, int thickness)
{
    // 确保图像是彩色图像
    if (image.channels() == 1) {
        cv::cvtColor(image, image, cv::COLOR_GRAY2BGR);
    }
    cv::rectangle(image, pt1, pt2, color, thickness);
}

void MainForm::drawTextOnImage(cv::Mat& image, const std::vector<std::string>& texts, cv::Point startPos, int fontFace, double fontScale, cv::Scalar color, int thickness, int lineSpacing)
{
    // 确保图像是彩色图像
    if (image.channels() == 1) {
        cv::cvtColor(image, image, cv::COLOR_GRAY2BGR);
    }

    // 复制起始位置(保护常量)
    cv::Point currentPos = startPos;

    for (const auto& text : texts) {
        cv::putText(image, text, currentPos, fontFace, fontScale, color, thickness);
        currentPos.y += lineSpacing;
    }
}

int MainForm::classifyWoodGrade(unsigned int** id, unsigned int* len) {
    // 每个面的状态：0=无缺陷，1=次要缺陷，2=主要缺陷，3=关键缺陷
    int faceSeverity[4] = {0};

    // 定义缺陷类型到严重等级的映射
    auto defectToSeverity = [](int defectId) -> int {
        // 关键缺陷: CRACK(4), MINERAL(5), PITH(3), BREAK(7)
        if (defectId == 3 || defectId == 4 || defectId == 5 || defectId == 7)
            return 3;
        // 主要缺陷: KNOT(2)
        if (defectId == 2)
            return 2;
        // 次要缺陷: LIGHTKNOT(1), SMALLKNOT(6), ROUGH(8)
        if (defectId == 1 || defectId == 6 || defectId == 8)
            return 1;
        // OK(0) 或未知缺陷
        return 0;
    };

    // 检查每个面，记录最高严重等级
    for (int face = 0; face < 4; face++) {
        for (int j = 0; j < len[face]; j++) {
            int severity = defectToSeverity(id[face][j]);
            if (severity > faceSeverity[face]) {
                faceSeverity[face] = severity;
            }
        }
    }

    // 统计各类缺陷面的数量
    int criticalFaces = 0; // 关键缺陷面数量
    int majorFaces = 0;    // 主要缺陷面数量
    int minorFaces = 0;    // 次要缺陷面数量

    for (int i = 0; i < 4; i++) {
        if (faceSeverity[i] == 3) criticalFaces++;
        else if (faceSeverity[i] == 2) majorFaces++;
        else if (faceSeverity[i] == 1) minorFaces++;
    }

    // 应用定级规则
    if (criticalFaces > 0) {
        return 4; // 有任何关键缺陷面
    } else if (majorFaces >= 2) {
        return 4; // 有2个或更多主要缺陷面
    } else if (majorFaces == 1) {
        return 3; // 仅有1个主要缺陷面
    } else if (minorFaces >= 3) {
        return 3; // 有3个或更多次要缺陷面
    } else if (minorFaces >= 1) {
        return 2; // 有1-2个次要缺陷面
    } else {
        return 1; // 无任何缺陷
    }
}

/**
 * 计算单木材的正材率（0\~100%）
 * @param id 缺陷ID数组（4个面）
 * @param len 每个面的缺陷数量
 * @return 正材率（百分比，double类型）
 */
double MainForm::calculateGoodWoodRatio(unsigned int** id, unsigned int* len) {
    int faceSeverity[4] = {0};  // 每个面的最高缺陷等级

    auto defectToSeverity = [](int defectId) -> int {
        if (defectId == 3 || defectId == 4 || defectId == 5 || defectId == 7) return 3;
        if (defectId == 2) return 2;
        if (defectId == 1 || defectId == 6 || defectId == 8) return 1;
        return 0;
    };

    // 计算每个面的最高缺陷等级
    for (int face = 0; face < 4; face++) {
        for (int j = 0; j < len[face]; j++) {
            int severity = defectToSeverity(id[face][j]);
            if (severity > faceSeverity[face]) {
                faceSeverity[face] = severity;
            }
        }
    }

    // 计算正材率（使用double）
    double goodWoodRatio = 0.0;
    for (int i = 0; i < 4; i++) {
        if (faceSeverity[i] <= 1) {  // 无缺陷或仅次要缺陷
            goodWoodRatio += 25.0;    // 每个面贡献25%
        }
    }

    return goodWoodRatio;
}
/**
 * 将缺陷置信度赋值到结构体字段
 * @param line 目标结构体（引用传递）
 * @param id 缺陷ID数组
 * @param confidence 置信度数组
 * @param len 每个面的缺陷数量
 */
void MainForm::assignDefectConfidenceToStruct(T_BatchLine &line, unsigned int **id, float **confidence, unsigned int *len)
{
    // 初始化所有缺陷字段为0
    line.wood_lk = 0;
    line.wood_dk = 0;
    line.wood_pt = 0;
    line.wood_cr = 0;
    line.wood_em = 0;
    line.wood_sm = 0;
    line.wood_bd = 0;
    line.wood_rs = 0;

    // 遍历所有缺陷
    for (int i = 0; i < 4; i++) {
        for (int j = 0; j < len[i]; j++) {
            int defectId = id[i][j];
            double conf = confidence[i][j];
            // 根据缺陷ID更新对应字段（取最大值）
            switch (defectId) {
            case 1: line.wood_lk = qMax(line.wood_lk, conf); break;
            case 2: line.wood_dk = qMax(line.wood_dk, conf); break;
            case 3: line.wood_pt = qMax(line.wood_pt, conf); break;
            case 4: line.wood_cr = qMax(line.wood_cr, conf); break;
            case 5: line.wood_em = qMax(line.wood_em, conf); break;
            case 6: line.wood_sm = qMax(line.wood_sm, conf); break;
            case 7: line.wood_bd = qMax(line.wood_bd, conf); break;
            case 8: line.wood_rs = qMax(line.wood_rs, conf); break;
            default: break; // 忽略无缺陷或未知ID
            }
        }
    }

    // // 转换为百分比
    // line.wood_lk *= 100;
    // line.wood_dk *= 100;
    // line.wood_pt *= 100;
    // line.wood_cr *= 100;
    // line.wood_em *= 100;
    // line.wood_sm *= 100;
    // line.wood_bd *= 100;
    // line.wood_rs *= 100;
}

void MainForm::calculateWoodDimensionsFromRects(const std::vector<cv::Rect> &woodRects, T_BatchLine &line, double dpi)
{
    double widths[4] = {0};
    double heights[4] = {0};
    double areas[4] = {0};
    bool hasValidFirstFace = false;
    int fallbackFaceIndex = -1;

    // 处理每个面的轮廓数据
    for (int i = 0; i < 4 && i < woodRects.size(); i++) {
        const cv::Rect& rect = woodRects[i];
        // 跳过无效矩形（宽或高为0）
        if (rect.width <= 0 || rect.height <= 0) {
            qWarning() << "Face" << i << "has invalid rect:" << rect.width << "x" << rect.height;
            continue;
        }

        // 计算物理尺寸（像素→毫米）
        widths[i] = pixelsToMm(rect.width, dpi);
        heights[i] = pixelsToMm(rect.height, dpi);
        areas[i] = widths[i] * heights[i];  // 面积（mm²）

        // 标记有效数据
        if (i == 0) {
            hasValidFirstFace = true;
            qDebug() << "Face 0 valid dimensions:" << widths[i] << "mm x" << heights[i] << "mm";
        } else if (fallbackFaceIndex == -1) {
            fallbackFaceIndex = i;
        }
    }

    // 决定使用哪个面的数据
    if (hasValidFirstFace) {
        // 优先使用第一个面数据（单位：mm）
        line.wood_length = roundToTwoDecimalPlaces(widths[0]);
        line.wood_width = roundToTwoDecimalPlaces(heights[0]);
        line.wood_line_area = roundToTwoDecimalPlaces(widths[0] * heights[0]);
        line.wood_wood_area = roundToTwoDecimalPlaces(areas[0] * 4);  // 假设对称，单面面积×4
    } else if (fallbackFaceIndex != -1) {
        // 使用第一个有效的备用面（单位：mm）
        line.wood_length = roundToTwoDecimalPlaces(widths[fallbackFaceIndex]);
        line.wood_width = roundToTwoDecimalPlaces(heights[fallbackFaceIndex]);
        line.wood_line_area = roundToTwoDecimalPlaces(widths[fallbackFaceIndex] * heights[fallbackFaceIndex]);
        line.wood_wood_area = roundToTwoDecimalPlaces(areas[fallbackFaceIndex] * 4);
        qWarning() << "Fallback to face" << fallbackFaceIndex << "due to invalid face 0";
    } else {
        // 所有面均无效
        line.wood_length = 0;
        line.wood_width = 0;
        line.wood_line_area = 0;
        line.wood_wood_area = 0;
        qWarning() << "All faces have invalid rects!";
    }
}

void MainForm::generateWoodInspectionReport(Detected_Data data)
{
    m_data.number += data.number;
    m_data.wood_ng += data.wood_ng;
    m_data.wood_speed = data.length;
    m_data.length += data.length;

    // qint64 totalTime = m_processStartTime - m_captureTime;

    ui->text_1->display(QString("%1").arg(data.length, 0, 'f', 2));
    ui->text_2->display(m_data.number);
    ui->text_3->display(m_data.wood_ng);
    // ui->text_4->display(QString("%1").arg(m_data.wood_speed /(double)algTotalTime, 0, 'f', 4));
    double rateOK =  1 - (double)m_data.wood_ng /(double)m_data.number;
    ui->text_5->display(QString("%1").arg(rateOK*100, 0, 'f', 2));


}

double MainForm::roundToTwoDecimalPlaces(double value)
{
    return std::round(value * 100.0) / 100.0;
}


void MainForm::checkAlgorithmResults()
{
    if(Obj<WoodInterFace>::GetInstance()->GetSignal() == 1) {
        m_algorithmEndTime = QDateTime::currentMSecsSinceEpoch();
        qint64 captureTime = m_processStartTime - m_captureTime;
        qint64 algTime = m_algorithmEndTime - m_processStartTime;
        Detected_Data recode;
        QImage::Format format;
        if(enableLocalMode){
            // Alg Test
            format = QImage::Format_BGR888;
        }else{
            format = QImage::Format_BGR888;
            recode.number =1;
        }
        QString strInfo;
        // 获取木材轮廓数据
        std::vector<cv::Rect> woodRects;
        Obj<WoodInterFace>::GetInstance()->GetWoodRect(woodRects);
        calculateWoodDimensionsFromRects(woodRects, m_Batchline);
        // 获取木材缺陷数据
        unsigned int** x1 = nullptr, ** y1 = nullptr, ** x2 = nullptr, ** y2 = nullptr, ** id = nullptr;
        float** confidence = nullptr;
        unsigned int* len = nullptr;
        // bool success = Obj<WoodInterFace>::GetInstance()->GetWoodBBox(x1, y1, x2, y2, id, confidence, len);
        std::vector<std::string> textLines;
        if (false) {
            int woodGrade = classifyWoodGrade(id, len);
            m_Batchline.wood_ok_rate = calculateGoodWoodRatio(id, len);
            assignDefectConfidenceToStruct(m_Batchline, id, confidence, len);
            switch (woodGrade) {
            case 1: textLines = {"Grade 1 (A)"};break;
            case 2: textLines = {"Grade 2 (B)"};break;
            case 3: textLines = {"Grade 3 (C)"};recode.wood_ng =1;break;
            case 4: textLines = {"Grade 4 (D)"};recode.wood_ng =1;break;
            default: textLines = {"Unknown Grade"};
            }
        }
        strInfo = QString("Capture Time: %1 ms, Algorithm Time: %2 ms").arg(captureTime).arg(algTime);
        textLines.push_back(strInfo.toStdString());
        for (int i = 0; i < LEN; i++) {
            void* imageData = nullptr;
            unsigned int width = 0, height = 0;
            // if (::GetWoodWholeImage(imageData, width, height, i)) {
            //     cv::Mat imgMat(height, width, CV_8UC3, imageData);

            //     if (!woodRects.empty())
            //     {
            //         drawRectangle(imgMat, woodRects[i]);
            //         double dWidth = pixelsToMm(woodRects[i].width, 96);
            //         double dHeight = pixelsToMm(woodRects[i].height, 96);
            //         double dArea = dWidth * dHeight;
            //         recode.length = dWidth / 100;
            //         strInfo = QString("W:%1\tH:%2\tA:%3\t")
            //                       .arg(dWidth, 0, 'f', 1)
            //                       .arg(dHeight, 0, 'f', 1)
            //                       .arg(dArea, 0, 'f', 1);
            //     }
            //     if (success) {
            //         strInfo += "\tDefects:";
            //         for (int j = 0; j < len[i]; j++) {
            //             int nId = id[i][j];
            //             float fConfig = confidence[i][j];

            //             QString strDefect;
            //             switch (nId) {
            //             case 0: strDefect = "OK"; break;
            //             case 1: strDefect = "LK"; break; // 简写
            //             case 2: strDefect = "KT"; break;
            //             case 3: strDefect = "PT"; break;
            //             case 4: strDefect = "CR"; break;
            //             case 5: strDefect = "MN"; break;
            //             case 6: strDefect = "SK"; break;
            //             case 7: strDefect = "BR"; break;
            //             case 8: strDefect = "RG"; break;
            //             default: strDefect = "UNKNOWN"; break;
            //             }
            //             strInfo += QString("[%1] %2 (%3%)\t")
            //                            .arg(j + 1)
            //                            .arg(strDefect)
            //                            .arg(static_cast<int>(fConfig * 100));
            //         }
            //     }
            //     // 使用默认位置绘制文字
            //     drawTextOnImage(imgMat, textLines);
            //     // 将Mat转换为QImage
            //     QImage img(imgMat.data,
            //                imgMat.cols,
            //                imgMat.rows,
            //                static_cast<int>(imgMat.step),
            //                format);
            //     emit m_woodProcessor->processingFinished(i + 1, img);
            //     delete[] static_cast<uchar*>(imageData);
            // }

            if(enableLocalMode){
            }else{
                switch (i) {
                case 0:ui->info_ccd_1->setText(strInfo);break;
                case 1:ui->info_ccd_2->setText(strInfo);break;
                case 2:ui->info_ccd_3->setText(strInfo);break;
                case 3:ui->info_ccd_4->setText(strInfo);break;
                default:
                    break;
                }
            }
        }
        // Remember to free all allocated memory when done
        /*for (unsigned int i = 0; i < LEN; ++i)
        {
            free(x1[i]);
            free(y1[i]);
            free(x2[i]);
            free(y2[i]);
            free(id[i]);
            free(confidence[i]);
        }
        free(len);
        delete[] x1;
        delete[] y1;
        delete[] x2;
        delete[] y2;
        delete[] id;
        delete[] confidence;*/
        // Obj<WoodInterFace>::GetInstance()->OnlineRelease();

        if(enableLocalMode){
        }else{
            generateWoodInspectionReport(recode);
        }

        QList<T_BatchLine> Batchlines;
        Batchlines.append(m_Batchline);
        for (auto& batchLine : Batchlines) {
            batchLine.wood_dk = roundToTwoDecimalPlaces(batchLine.wood_dk);
            batchLine.wood_lk = roundToTwoDecimalPlaces(batchLine.wood_lk);
            batchLine.wood_cr = roundToTwoDecimalPlaces(batchLine.wood_cr);
            batchLine.wood_hb = roundToTwoDecimalPlaces(batchLine.wood_hb);
            batchLine.wood_em = roundToTwoDecimalPlaces(batchLine.wood_em);
            batchLine.wood_wh = roundToTwoDecimalPlaces(batchLine.wood_wh);
            batchLine.wood_sm = roundToTwoDecimalPlaces(batchLine.wood_sm);
            batchLine.wood_bd = roundToTwoDecimalPlaces(batchLine.wood_bd);
            batchLine.wood_pt = roundToTwoDecimalPlaces(batchLine.wood_pt);
            batchLine.wood_rs = roundToTwoDecimalPlaces(batchLine.wood_rs);
        }
        if (m_sql.insertBatchLines(Batchlines)) {
            qDebug() << "insert success!";
        }
        // ::OnlineRelease();
    }
}

void MainForm::updateSaveStates(int index)
{
    switch (index) {
    case 1:m_tSaveImg1->m_worker->m_cSave = m_SaveForm->m_saveSw.m_cSave1;break;
    case 2:m_tSaveImg2->m_worker->m_cSave = m_SaveForm->m_saveSw.m_cSave2;break;
    case 3:m_tSaveImg3->m_worker->m_cSave = m_SaveForm->m_saveSw.m_cSave3;break;
    case 4:m_tSaveImg4->m_worker->m_cSave = m_SaveForm->m_saveSw.m_cSave4;break;
    case 5:m_tSaveImg5->m_worker->m_cSave = m_SaveForm->m_saveSw.m_cSave5;break;
    case 6:m_tSaveImg6->m_worker->m_cSave = m_SaveForm->m_saveSw.m_cSave6;break;
    default:
        break;
    }
}

void MainForm::slotAlgTestPath(const QFileInfoList& fileList)
{
    m_processStartTime = QDateTime::currentMSecsSinceEpoch();
    enableLocalMode = true;

    
    std::vector<cv::Mat> images;
    images.reserve(LEN);

    
    for (int i = 0; i < LEN; ++i) images.emplace_back();   // 空 Mat

    int woodFrameIndex = 1;   // 去掉成员前缀，避免名字遮蔽

    for (const auto& item : fileList)
    {
        // 直接读入灰度或所需通道，减少解码后通道转换
        cv::Mat src = cv::imread(item.filePath().toLocal8Bit().constData(),
            cv::IMREAD_COLOR);   // 或 IMREAD_GRAYSCALE
        if (src.empty()) {
            qDebug() << "wood", "imread failed: " + item.filePath();
            continue;
        }

        // 用 assign 复用 vector 容量，只改 Mat 的 header
        std::fill(images.begin(), images.end(), src);   // 浅拷贝 header，无像素拷贝

        //  计算 woodFlag
        int woodFlag = (woodFrameIndex == 1) ? WoodFront :
            (woodFrameIndex == fileList.size()) ? WoodRear :
            WoodMidt;

        // 处理
        try {
            m_woodProcessor->processWoodImages(images, woodFlag);
        }
        catch (const std::exception& e) {
            qDebug() << "Processing failed: " << e.what();
            throw;
        }
        ++woodFrameIndex;
    }
}

void MainForm::slotBatcnInfo(int batchId)
{
    m_nBatchId = batchId;
    T_Batch batchData = m_sql.queryBatchs(m_nBatchId);
    T_PlanLine planLine = m_sql.queryPlanLine(batchData.plan_id);
    ui->label_batch->setText(batchData.batch_name);
    ui->label_plan->setText(batchData.plan_name);

    m_Batchline.batch_id = m_nBatchId;
    m_Batchline.wood_type = planLine.woodType;
}

void MainForm::on_Bt_start_clicked()
{
    enableLocalMode = false;
    if(m_nStart){
        ui->Bt_start->setIconSize(QSize(30,30));
        ui->Bt_start->setIcon(QIcon(":/imgs/stop.png"));
        ui->Bt_start->setText(tr("stop"));


        m_tCamera1->start();
        m_tCamera2->start();
        m_tCamera3->start();
        m_tCamera4->start();
        m_tCamera5->start();
        m_tCamera6->start();
        emit sigToMotor(1);
    }else{
        ui->Bt_start->setIconSize(QSize(30,30));
        ui->Bt_start->setIcon(QIcon(":/imgs/start.png"));
        ui->Bt_start->setText(tr("start"));


        m_tCamera1->stop();
        m_tCamera2->stop();
        m_tCamera3->stop();
        m_tCamera4->stop();
        m_tCamera5->stop();
        m_tCamera6->stop();

        emit sigToMotor(0);
    }
    m_nStart = 1 - m_nStart;
}


void MainForm::on_Bt_Save_clicked()
{
    m_SaveForm->show();
}



void MainForm::on_Bt_CreatBatch_clicked()
{
    DataForm* m_pBatch = new DataForm();
    m_pBatch->setObjectName("m_pBatch");
    m_pBatch->setBatchPage();
    m_pBatch->show();
    connect(m_pBatch,&DataForm::sendBatchInfo,this,&MainForm::slotBatcnInfo);
}

void MainForm::readAreaCameraConfig()
{
    QString Path = "configs/configs.ini";
    QSettings iniRead(Path, QSettings::IniFormat);
    iniRead.setIniCodec(QTextCodec::codecForName("UTF_8"));

    // 相机5的ROI (对应Roi_setting_1)
    int x5 = iniRead.value("Roi_setting_1/x", 540).toInt();
    int y5 = iniRead.value("Roi_setting_1/y", 551).toInt();
    int w5 = iniRead.value("Roi_setting_1/width", 493).toInt();
    int h5 = iniRead.value("Roi_setting_1/height", 485).toInt();
    m_areaRoi5 = QRect(x5, y5, w5, h5);

    // 相机6的ROI (对应Roi_setting_2)
    int x6 = iniRead.value("Roi_setting_2/x", 130).toInt();
    int y6 = iniRead.value("Roi_setting_2/y", 537).toInt();
    int w6 = iniRead.value("Roi_setting_2/width", 1010).toInt();
    int h6 = iniRead.value("Roi_setting_2/height", 544).toInt();
    m_areaRoi6 = QRect(x6, y6, w6, h6);
}

